-
Notifications
You must be signed in to change notification settings - Fork 140
feat: add expo plugin and trusted origins #597
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: v3-main
Are you sure you want to change the base?
Conversation
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
WalkthroughAdds the Changes
Sequence Diagram(s)sequenceDiagram
participant ExpoApp as Client (Expo App)
participant Server as Server (app)
participant BetterAuth as BetterAuth core
participant ExpoPlugin as @better-auth/expo
ExpoApp->>Server: Start auth flow / callback
Server->>BetterAuth: Invoke auth processing (plugins enabled)
BetterAuth->>ExpoPlugin: Delegate Expo-specific handling
ExpoPlugin-->>BetterAuth: Return plugin result
BetterAuth-->>Server: Return auth result (validated against trustedOrigins)
Server-->>ExpoApp: Complete auth response
Estimated code review effort🎯 2 (Simple) | ⏱️ ~8 minutes ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yaml
is excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (2)
app/server/auth.tsx
(3 hunks)package.json
(1 hunks)
🧰 Additional context used
🧠 Learnings (1)
app/server/auth.tsx (1)
Learnt from: ivan-dalmet
PR: BearStudio/start-ui-web#532
File: src/server/config/oauth/providers/discord.ts:11-11
Timestamp: 2024-10-14T15:29:53.279Z
Learning: In `src/server/config/oauth/providers/discord.ts`, when defining the `zDiscordUser` schema, keep fields like `username` as nullable (e.g., `.nullish()`), as the Discord API might return null values despite the documentation stating otherwise.
🧬 Code Graph Analysis (1)
app/server/auth.tsx (1)
app/env/server.ts (1)
envServer
(9-33)
🪛 ESLint
app/server/auth.tsx
[error] 21-21: 'inferAdditionalFields' is defined but never used. Allowed unused vars must match /^_/u.
(@typescript-eslint/no-unused-vars)
[error] 21-21: Remove this unused import of 'inferAdditionalFields'.
(sonarjs/unused-import)
🔇 Additional comments (4)
package.json (1)
47-47
: LGTM - Dependency addition looks correct.The
@better-auth/expo
dependency is properly added and the version aligns well with the existingbetter-auth
ecosystem in the project.app/server/auth.tsx (3)
5-5
: LGTM - Expo plugin import correctly added.The import is properly used in the plugins array configuration.
29-29
: LGTM - Trusted origins configuration is appropriate.The trusted origins configuration with
start-ui-native://
andstart-ui-native://*
properly allows the native Expo app to authenticate with the web server. This follows the expected pattern for Expo deep linking authentication flows.
54-54
: LGTM - Expo plugin correctly integrated.The
expo()
plugin is properly added to the plugins array and will enable Expo-specific authentication functionality.
app/server/auth.tsx
Outdated
@@ -17,13 +18,15 @@ import { permissions } from '@/features/auth/permissions'; | |||
import { db } from '@/server/db'; | |||
import { sendEmail } from '@/server/email'; | |||
import { getUserLanguage } from '@/server/utils'; | |||
import { inferAdditionalFields } from 'better-auth/client/plugins'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove unused import.
The inferAdditionalFields
import is not used anywhere in the code and should be removed to keep the codebase clean.
-import { inferAdditionalFields } from 'better-auth/client/plugins';
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
import { inferAdditionalFields } from 'better-auth/client/plugins'; |
🧰 Tools
🪛 ESLint
[error] 21-21: 'inferAdditionalFields' is defined but never used. Allowed unused vars must match /^_/u.
(@typescript-eslint/no-unused-vars)
[error] 21-21: Remove this unused import of 'inferAdditionalFields'.
(sonarjs/unused-import)
🤖 Prompt for AI Agents
In app/server/auth.tsx at line 21, the import statement for
inferAdditionalFields from better-auth/client/plugins is unused. Remove this
import line entirely to clean up the code and avoid unnecessary imports.
123ca57
to
ccdf949
Compare
8776fa0
to
3e9f56d
Compare
|
cf02ee7
to
cbaf95e
Compare
3e9f56d
to
d7d6ac9
Compare
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
.env.example
(1 hunks)app/env/server.ts
(1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Playwright E2E Tests
@@ -23,6 +23,7 @@ DATABASE_URL="postgres://${DOCKER_DATABASE_USERNAME}:${DOCKER_DATABASE_PASSWORD} | |||
BETTER_AUTH_SECRET="REPLACE ME" # You can use `npx @better-auth/cli@latest secret` to a generated secret | |||
SESSION_EXPIRATION_IN_SECONDS=2592000 # 30 days | |||
SESSION_UPDATE_AGE_IN_SECONDS=86400 # 1 day (every 1 day the session expiration is updated) | |||
AUTH_TRUSTED_ORIGIN="start-ui-native://*" # Mobile app scheme for trustedOrigins config |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Align env var with array semantics and allow multiple trusted origins (CSV).
better-auth expects an array for trustedOrigins. A single AUTH_TRUSTED_ORIGIN is limiting and semantically singular. Consider switching to a plural, comma-separated env to support multiple entries out of the box.
Apply this diff to the example:
-AUTH_TRUSTED_ORIGIN="start-ui-native://*" # Mobile app scheme for trustedOrigins config
+AUTH_TRUSTED_ORIGINS="start-ui-native://,start-ui-native://*" # Mobile app schemes for trustedOrigins config (CSV)
Follow-up:
- Keep backward compatibility by reading both AUTH_TRUSTED_ORIGINS (CSV) and legacy AUTH_TRUSTED_ORIGIN (single), if you adopt the change in the code.
- Ensure app/server/auth.tsx parses CSV when building the trustedOrigins array.
Run to locate all usages and assess impact (rename vs. dual support):
Additionally, verify whether better-auth accepts the wildcard form for custom schemes or if plain "scheme://" is sufficient:
🏁 Script executed:
#!/bin/bash
# Find occurrences of the old and proposed env names and trustedOrigins usage
rg -n -A2 -B2 $'AUTH_TRUSTED_ORIGIN\\b|AUTH_TRUSTED_ORIGINS\\b|trustedOrigins\\s*:'
Length of output: 756
🌐 Web query:
What formats does better-auth accept for the `trustedOrigins` option, and are custom URL schemes (e.g., start-ui-native:// or start-ui-native://*) supported?
💡 Result:
Better Auth accepts the following formats for the trustedOrigins
option:
- An array of static origin strings (e.g., "https://example.com")
- A function that returns a list of origins dynamically
- Wildcard patterns (including domain wildcards and protocol wildcards)
- Custom URL schemes such as
start-ui-native://
for deep links and mobile authentication flows[1][3][4].
Custom URL Schemes Support:
- Trusted origins can use custom schemes like
start-ui-native://
to enable deep linking for mobile or desktop apps[3][4]. - You can list your custom scheme directly (e.g.,
"start-ui-native://"
). - Wildcards with custom schemes are supported when you have varying paths or want to match all routes within the scheme (e.g.,
"start-ui-native://*"
)[3]. - This pattern is especially useful for Expo, React Native, and similar apps leveraging deep links after authentication[3].
Illustrative Example:
trustedOrigins: [
"https://yourdomain.com",
"start-ui-native://",
"start-ui-native://*",
"myapp-extension://*"
]
- "start-ui-native://" allows exact matches.
- "start-ui-native://*" matches any path or resource following the custom scheme, which is necessary if your app handles multiple deep-linked routes[3].
Key Details:
- Wildcards can be used after both standard protocols and custom schemes, but the scheme itself (e.g.,
start-ui-native
) must match exactly—wildcarding does not change the protocol/scheme matching logic[4]. - For web origins, both domain and protocol wildcards are also supported (e.g.,
"https://*.example.com"
,"*.example.com"
), with HTTPS preferred for security[4][1]. - Custom schemes are intended for apps and browser extensions; for browsers, redirection to custom schemes usually must be handled client-side for security reasons[8].
References:
- Full documentation: Better Auth config reference[1], security and trusted origins explanation[4], and Expo integration docs highlighting custom schemes and wildcards[3].
- Client-side handling for redirects with custom schemes: GitHub discussion[8].
In summary:
Better Auth’s trustedOrigins
can accept static URLs, dynamic lists, wildcard patterns, and custom URL schemes (including wildcards such as start-ui-native://*
). Custom URL schemes are a first-class feature for deep linking in mobile and desktop apps and should be explicitly listed in your configuration for safe origin handling[3][4].
Citations:
- 1: https://www.better-auth.com/docs/reference/options
- 2: https://www.better-auth.com/docs/concepts/database
- 3: https://www.better-auth.com/docs/integrations/expo
- 4: https://www.better-auth.com/docs/reference/security
- 5: https://www.better-auth.com
- 6: https://www.better-auth.com/docs/concepts/client
- 7: How to configure multiple domains? better-auth/better-auth#2296
- 8: google authentication w/ mcp plugin not triggering redirect to url better-auth/better-auth#3714
- 9: https://better-auth-ui.com/advanced/custom-settings
- 10: https://help.okta.com/en-us/content/topics/security/api-trusted-origins.htm
Align AUTH_TRUSTED_ORIGINS for multiple origins
To support an array of trusted origins while preserving backward-compatibility:
• .env.example
-AUTH_TRUSTED_ORIGIN="start-ui-native://*" # Mobile app scheme for trustedOrigins config
+AUTH_TRUSTED_ORIGINS="start-ui-native://,start-ui-native://*" # Mobile app schemes for trustedOrigins config (CSV)
• app/env/server.ts
– Replace AUTH_TRUSTED_ORIGIN: z.string()
– Add AUTH_TRUSTED_ORIGINS: z.string().default('').transform(s => s.split(',').filter(Boolean))
– In your server config, coalesce:
const rawOrigins = env.AUTH_TRUSTED_ORIGINS.length
? env.AUTH_TRUSTED_ORIGINS
: [env.AUTH_TRUSTED_ORIGIN];
• app/server/auth.tsx
– Change
trustedOrigins: [envServer.AUTH_TRUSTED_ORIGIN],
to
trustedOrigins: rawOrigins,
Ensure you read the new AUTH_TRUSTED_ORIGINS
CSV first and fall back to the legacy AUTH_TRUSTED_ORIGIN
for a smooth migration.
🤖 Prompt for AI Agents
In .env.example around line 26, the current AUTH_TRUSTED_ORIGIN entry only
supports a single origin; update the app to accept multiple origins by adding a
CSV-based AUTH_TRUSTED_ORIGINS variable and adjusting server parsing and usage:
in app/env/server.ts replace the single z.string() validator with a
z.string().default('').transform to split on commas and filter empties, and
coalesce at runtime to use AUTH_TRUSTED_ORIGINS when present or fall back to the
legacy AUTH_TRUSTED_ORIGIN; then update app/server/auth.tsx to pass the computed
rawOrigins to trustedOrigins instead of the single envServer.AUTH_TRUSTED_ORIGIN
so both multi-origin CSV and the old single value remain supported during
migration.
@@ -12,6 +12,7 @@ export const envServer = createEnv({ | |||
BETTER_AUTH_SECRET: z.string(), | |||
SESSION_EXPIRATION_IN_SECONDS: z.coerce.number().int().default(2592000), // 30 days by default | |||
SESSION_UPDATE_AGE_IN_SECONDS: z.coerce.number().int().default(86400), // 1 day by default | |||
AUTH_TRUSTED_ORIGIN: z.string(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
Provide a sane default and reduce friction in local dev.
AUTH_TRUSTED_ORIGIN is required with no default, while .env.example suggests "start-ui-native://*". Add a default to keep parity and avoid failures when the env is not set.
Apply this diff:
- AUTH_TRUSTED_ORIGIN: z.string(),
+ AUTH_TRUSTED_ORIGIN: z.string().default('start-ui-native://*'),
Optional (future-proofing): if you decide to support multiple origins via CSV as suggested in .env.example, consider this alternative and adjust downstream code to expect an array:
// Optionally add alongside AUTH_TRUSTED_ORIGIN for backwards compatibility:
AUTH_TRUSTED_ORIGINS: z
.string()
.default('start-ui-native://,start-ui-native://*')
.transform((v) =>
v
.split(',')
.map((s) => s.trim())
.filter(Boolean),
),
Confirm that app/server/auth.tsx actually consumes envServer.AUTH_TRUSTED_ORIGIN (or the proposed AUTH_TRUSTED_ORIGINS) when building the betterAuth trustedOrigins array, and that you’re not unintentionally hardcoding values:
🏁 Script executed:
#!/bin/bash
# Check usage of trustedOrigins and whether the env var is wired through
rg -n -A5 -B5 $'trustedOrigins\\s*:|AUTH_TRUSTED_ORIGIN\\b|AUTH_TRUSTED_ORIGINS\\b' app
Length of output: 1339
Provide a sane default for AUTH_TRUSTED_ORIGIN in your env schema
To match your .env.example
and avoid runtime failures when AUTH_TRUSTED_ORIGIN
isn’t set, add a default:
• File: app/env/server.ts
(line 15)
- AUTH_TRUSTED_ORIGIN: z.string(),
+ AUTH_TRUSTED_ORIGIN: z.string().default('start-ui-native://*'),
Optional future-proofing (support multiple origins via CSV):
AUTH_TRUSTED_ORIGINS: z
.string()
.default('start-ui-native://,start-ui-native://*')
.transform(v =>
v
.split(',')
.map(s => s.trim())
.filter(Boolean),
)
Note: app/server/auth.tsx
already uses envServer.AUTH_TRUSTED_ORIGIN
in its trustedOrigins
array, so this default will be picked up automatically.
🤖 Prompt for AI Agents
In app/env/server.ts around line 15, the env schema requires AUTH_TRUSTED_ORIGIN
but provides no default which can cause runtime failures when the variable is
not set; update the zod schema to supply a sensible default (matching
.env.example) — e.g. set AUTH_TRUSTED_ORIGIN to a default like
'start-ui-native://,start-ui-native://*' or alternatively add
AUTH_TRUSTED_ORIGINS as a CSV string with a transform to produce an array of
trimmed, non-empty origins — ensure the schema uses .default(...) and, if using
the CSV approach, .transform(...) to split, trim and filter values so
app/server/auth.tsx can safely read trusted origins without runtime errors.
@@ -24,6 +25,7 @@ export const auth = betterAuth({ | |||
expiresIn: envServer.SESSION_EXPIRATION_IN_SECONDS, | |||
updateAge: envServer.SESSION_UPDATE_AGE_IN_SECONDS, | |||
}, | |||
trustedOrigins: [envServer.AUTH_TRUSTED_ORIGIN], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A comment to explain the purpose of this line would be great so people not using Start UI [native] will know that they can remove it.
Describe your changes
Add expo plugin and trusted origins to
better-auth
config, to make socials login works on start ui native using start ui web serverSummary by CodeRabbit
New Features
Chores